/* tinycap.c
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
**     * Redistributions of source code must retain the above copyright
**       notice, this list of conditions and the following disclaimer.
**     * Redistributions in binary form must reproduce the above copyright
**       notice, this list of conditions and the following disclaimer in the
**       documentation and/or other materials provided with the distribution.
**     * Neither the name of The Android Open Source Project nor the names of
**       its contributors may be used to endorse or promote products derived
**       from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
*/

#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "tinycap.h"
#include "audioutils/resampler.h"
#include "include/Log.h"
#include "AudioChannelObserver.h"
#include <sys/system_properties.h>

#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT  0x20746d66
#define ID_DATA 0x61746164

#define FORMAT_PCM 1

#define PROPERTY_VALUE_MAX 92
static char board_platform[PROPERTY_VALUE_MAX] = { 0 };
static char bluetooth_call[PROPERTY_VALUE_MAX] = { 0 };
static char set_4490_size[PROPERTY_VALUE_MAX] = { 0 };
static char dump_tinycap[PROPERTY_VALUE_MAX] = { 0 };

FunctionAudioCallBack mAudioCallback = NULL;

void setAudioRecordCallBack(FunctionAudioCallBack callback)
{
	mAudioCallback = callback;
}
typedef struct AudioContext{
	struct resampler_itfe *resampler;
	int channels;
	int inSampleRate;
	int outSampleRate;
	int audioType;
	int bit;
}PlayAudioContext;

PlayAudioContext mPlayAudioContext;

int currentAudioType = 0;
int outputChannels = 1;
int outputSampleRate = 48000;
int outputBits = 16;

struct wav_header {
    uint32_t riff_id;
    uint32_t riff_sz;
    uint32_t riff_fmt;
    uint32_t fmt_id;
    uint32_t fmt_sz;
    uint16_t audio_format;
    uint16_t num_channels;
    uint32_t sample_rate;
    uint32_t byte_rate;
    uint16_t block_align;
    uint16_t bits_per_sample;
    uint32_t data_id;
    uint32_t data_sz;
};

int capturing = 1;
int tinycapSave = 0;
pthread_t	 inputAudioThread;          // Thread to offload sending audio data.
pthread_t *	 inputAudioThreadPtr;       // Ptr to sendAudioThread when valid.

unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
                            unsigned int channels, unsigned int rate,
                            enum pcm_format format, unsigned int period_size,
                            unsigned int period_count, unsigned int cap_time);

void sigint_handler(int sig __unused)
{
    capturing = 0;
}

int tinycat_loop(int argc, char **argv)
{
	LOGD("txz tinycat_loop");
	if( currentAudioType == AUDIO_CHANNEL_SPEECH)
	{
		__system_property_set("sys.lynx.txz.channel", "1");
	}
    FILE *file = NULL;
    struct wav_header header;
    unsigned int card = 0;
    unsigned int device = 0;

    unsigned int channels = 2;
    unsigned int rate = 48000;
    unsigned int bits = 16;
    unsigned int frames;
//    unsigned int period_size = 1024;
    unsigned int period_size = 128; //Garmin 专用 256
    unsigned int period_count = 4;
    unsigned int cap_time = 0;
    enum pcm_format format;

    __system_property_get("ro.board.platform", board_platform);
    __system_property_get("sys.lynx.bt.mt8163", bluetooth_call);
    __system_property_get("persist.lynx.4490.period", set_4490_size);
    if (strcmp(board_platform, "mt8163") == 0) {
    	//mtk mt8163
    	device = 9; //小d是 device
    	card = 0; //大D是card
    }
    else if (strcmp(board_platform, "parrot") == 0) {
    	//4490
    	device = 0; //小d是 device
    	card = 2; //大D是card
    	LOGD("tinycap_start set_4490_size = %s",set_4490_size);

    	period_size = atoi(set_4490_size);
    	LOGD("tinycap_start set 4490 period_size = %d",period_size);
    }


     if (argc < 2) {
        fprintf(stderr, "Usage:  file.wav [-D card] [-d device]"
                " [-c channels] [-r rate] [-b bits] [-p period_size]"
                " [-n n_periods] [-T capture time]\n");
        //return 1;
    } 

    if( tinycapSave == 1 )
    {
//        file = fopen("/data/tinycap.pcm", "wb");
        file = fopen("/sdcard/.debug/tinycap.pcm", "wb");
        if (!file) {
            fprintf(stderr, "Unable to create file '%s'\n", argv[1]);
            return 1;
        }
    }
    switch (bits) {
    case 32:
        format = PCM_FORMAT_S32_LE;
        break;
    case 24:
        format = PCM_FORMAT_S24_LE;
        break;
    case 16:
        format = PCM_FORMAT_S16_LE;
        break;
    default:
        fprintf(stderr, "%u bits is not supported.\n", bits);
        if(file != NULL )
        {
        	fclose(file);
        }
        return 1;
    }

    /* install signal handler and begin capturing */
    signal(SIGINT, sigint_handler);
    signal(SIGHUP, sigint_handler);
    signal(SIGTERM, sigint_handler);
    frames = capture_sample(file, card, device, 2,
                            rate, format,
                            period_size, period_count, cap_time);
    printf("Captured %u frames\n", frames);

    /* write header now all information is known */
/*     header.data_sz = frames * header.block_align;
    header.riff_sz = header.data_sz + sizeof(header) - 8;
    fseek(file, 0, SEEK_SET);
    fwrite(&header, sizeof(struct wav_header), 1, file); */

    if( file != NULL )
    {
    	fclose(file);
    }
    return 0;
}

static int PrintCurrentSpeaker(int length){
 static long   length_1s   = 0;
 static time_t old_time    = 0;
 time_t  now_time    = time(NULL);
 if(old_time == 0) old_time = now_time;
 length_1s += length;
 if((now_time - old_time) >= 1){
  printf("speaker out:%ld byte/s", length_1s/(now_time - old_time));
  old_time = now_time;
  length_1s = 0;
 }
   return 0;
}

unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
                            unsigned int channels, unsigned int rate,
                            enum pcm_format format, unsigned int period_size,
                            unsigned int period_count, unsigned int cap_time)
{
    struct pcm_config config;
    struct pcm *pcm;
    char *buffer;
    unsigned int size;
    unsigned int bytes_read = 0;
    unsigned int frames = 0;
    struct timespec end;
    struct timespec now;
    memset(&config, 0, sizeof(config));
    config.channels = channels;
    config.rate = rate;
    config.period_size = period_size;
    config.period_count = period_count;
    config.format = format;
    config.start_threshold = 0;
    config.stop_threshold = 0;
    config.silence_threshold = 0;

    pcm = pcm_open(card, device, PCM_IN, &config);
    if (!pcm || !pcm_is_ready(pcm)) {
        fprintf(stderr, "Unable to open PCM device (%s)\n",
                pcm_get_error(pcm));
        return 0;
    }

    size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
    LOGD("tinycap_start setsize = %d",size);
//    size /= 4;
    buffer = malloc(size);
    if (!buffer) {
        fprintf(stderr, "Unable to allocate %u bytes\n", size);
        free(buffer);
        pcm_close(pcm);
        return 0;
    }

    printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate,
           pcm_format_to_bits(format));

    clock_gettime(CLOCK_MONOTONIC, &now);
    end.tv_sec = now.tv_sec + cap_time;
    end.tv_nsec = now.tv_nsec;

    ///add for box
    size_t in_frames = 0;
    size_t out_frames = 0;
    int i = 0;
    int16_t *pBuffer = NULL;

    while (capturing && !pcm_read(pcm, buffer, size)) {
    	if(mAudioCallback!=NULL)
    	{
			if(tinycapSave == 1)
			{
    			if (fwrite(buffer, 1, size, file) != (size)) {
    			            fprintf(stderr,"Error capturing sample\n");
    			 }
			}
    		int out_byte_size = size;
    		if( outputSampleRate != 48000 )
    		{
    			//需要重采样
    			in_frames = size / (2 * 16 / 8);
    			out_frames = ((float)in_frames) / ((float)48000/(float)outputSampleRate);
    			out_byte_size = out_frames * (2 * 16 / 8);
    		}
    		char out_byte_buffer [ out_byte_size ];
    		memset(out_byte_buffer,0,out_byte_size);

    		if(mPlayAudioContext.resampler)
    		{
        			//读取到数据buffer,长度size
        			mPlayAudioContext.resampler->resample_from_input(mPlayAudioContext.resampler,
    		                                   (int16_t *)buffer,
    		                                   &in_frames,
    		                                   (int16_t *)out_byte_buffer,
    		                                   &out_frames);
//        			if(tinycapSave == 1)
//        			{
//            			if (fwrite(out_byte_buffer, 1, out_byte_size, file) != (out_byte_size)) {
//            			            fprintf(stderr,"Error capturing sample\n");
//            			 }
//        			}
    		}else{
    			memcpy(out_byte_buffer,buffer,out_byte_size);
    		}

    		if( outputChannels == 1)
    		{
    			//单声道输出
				int out_mono_size = out_byte_size / 2;
				char out_mono_buffer [ out_mono_size ];
				for(int i=0; i<out_mono_size; i++)
				{
					if (i % 2 == 0)
					{
						memcpy(out_mono_buffer + i, out_byte_buffer + i * 2, 2);
					}
				}
    			//frames 音频数据帧大小 frames = len / (bits / 8 * channels)
    			int frames = out_mono_size/(16 / 8 * 1);
    			mAudioCallback(out_mono_buffer,out_mono_size,frames);

    		}else {
    			int frames = out_byte_size/(16 / 8 * 2);
    			mAudioCallback(out_byte_buffer,out_byte_size,frames);
    		}


    	}
        bytes_read += size;
    }

    frames = pcm_bytes_to_frames(pcm, bytes_read);
    free(buffer);
    pcm_close(pcm);
    return frames;
}

static void *	_GeneralPcmThread( void *inArg )
{
//	system("loopback.sh");
//	usleep(100*1000);
	capturing = 1;
	tinycat_loop(0,NULL);
	return( NULL );
}

int tinycap_set_parameter(int type,int sampleRate,int bits,int channels){
	outputChannels = channels;
	outputSampleRate = sampleRate;
	outputBits = bits;
	return 0;
}
int tinycap_start(int type){
	LOGD("tinycap_start audioType = %d, outputSampleRate = %d, outputChannels = %d\n",type,outputSampleRate,outputChannels);
	currentAudioType = type;

	__system_property_get("sys.lynx.shark.dump_tinycap", dump_tinycap);
	if(strlen( dump_tinycap ) > 0 )
	{
		tinycapSave = atoi(dump_tinycap);
	}
	remove( "/sdcard/.debug/tinycap.pcm" );
//	tinycapSave = 1;
	if( outputSampleRate != 48000 )
	{
		mPlayAudioContext.audioType = type;
		mPlayAudioContext.inSampleRate = 48000;
		mPlayAudioContext.outSampleRate = outputSampleRate;
		mPlayAudioContext.bit = 16;
		mPlayAudioContext.channels = 2;
		int ret = create_resampler(mPlayAudioContext.inSampleRate,
				mPlayAudioContext.outSampleRate,
				mPlayAudioContext.channels, RESAMPLER_QUALITY_DESKTOP, NULL, &mPlayAudioContext.resampler);
	}
	volatile int	err;
	err = pthread_create( &inputAudioThread, NULL, _GeneralPcmThread, NULL );
	if(err == 0)
	{
		inputAudioThreadPtr = &inputAudioThread;
	}
	return err;
}

int tinycap_stop(int type){
	LOGD("tinycap_stop audioType = %d \n",type);
	if(outputSampleRate != 48000 )
	{
		if( mPlayAudioContext.resampler != NULL )
		{
			release_resampler(mPlayAudioContext.resampler);
			mPlayAudioContext.resampler = NULL;
		}
	}
	capturing = 0;
	if( inputAudioThreadPtr )
	{
		pthread_join( inputAudioThread, NULL );
		inputAudioThreadPtr = NULL;
	}
	if( type == AUDIO_CHANNEL_SPEECH)
	{
		__system_property_set("sys.lynx.txz.channel", "0");
	}
	return 0;
}
